﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace DynamicGeometry
{
    public abstract class Behavior
    {
        private static ReadOnlyCollection<Behavior> mSingletons;
        public static ReadOnlyCollection<Behavior> Singletons
        {
            get
            {
                if (mSingletons == null)
                {
                    mSingletons = InitializeBehaviors();
                }
                return mSingletons;
            }
        }

        private static ReadOnlyCollection<Behavior> InitializeBehaviors()
        {
            List<Behavior> result = new List<Behavior>();
            Type basic = typeof(Behavior);

            foreach (Type t in basic.Assembly.GetTypes())
            {
                if (basic.IsAssignableFrom(t) && !t.IsAbstract)
                {
                    Behavior instance = Activator.CreateInstance(t) as Behavior;
                    result.Add(instance);
                }
            }

            BehaviorOrderer.Order(result);

            return result.AsReadOnly();
        }

        public static Behavior LookupByName(string b)
        {
            return Singletons.Where(x => x.Name == b).FirstOrDefault();
        }

        public abstract string Name
        {
            get;
        }

        public virtual string HintText
        {
            get
            {
                return "";
            }
        }

        public virtual Color ToolButtonColor
        {
            get
            {
                return Color.FromArgb(255, 200, 200, 200);
            }
        }

        public static double IconSize
        {
            get
            {
                return 32;
            }
        }

        public Panel Icon
        {
            get
            {

                return CreateIcon();
            }
        }

        public virtual Panel CreateIcon()
        {
            return IconBuilder.BuildIcon()
                .Point(0.5, 0.5)
                .Canvas;
        }

        private Drawing mDrawing;
        public Drawing Drawing
        {
            get
            {
                return mDrawing;
            }
            set
            {
                if (mDrawing != null)
                {
                    mDrawing.OnAttachToCanvas -= mDrawing_OnAttachToCanvas;
                    mDrawing.OnDetachFromCanvas -= mDrawing_OnDetachFromCanvas;
                    Stopping();
                    Parent = null;
                }
                mDrawing = value;
                if (mDrawing != null)
                {
                    mDrawing.OnAttachToCanvas += mDrawing_OnAttachToCanvas;
                    mDrawing.OnDetachFromCanvas += mDrawing_OnDetachFromCanvas;
                    Started();
                    Parent = mDrawing.Canvas;
                }
            }
        }

        void mDrawing_OnAttachToCanvas(Canvas e)
        {
            Parent = e;
        }

        void mDrawing_OnDetachFromCanvas(Canvas e)
        {
            Parent = null;
        }

        private Canvas mParent;
        protected Canvas Parent
        {
            get
            {
                return mParent;
            }
            set
            {
                if (mParent != null)
                {
                    mParent.MouseLeftButtonDown -= MouseDown;
                    mParent.MouseMove -= MouseMove;
                    mParent.MouseLeftButtonUp -= MouseUp;
                    mParent.KeyDown -= KeyDown;
                    mParent.KeyUp -= KeyUp;
                }
                mParent = value;
                if (mParent != null)
                {
                    mParent.MouseLeftButtonDown += MouseDown;
                    mParent.MouseMove += MouseMove;
                    mParent.MouseLeftButtonUp += MouseUp;
                    mParent.KeyDown += KeyDown;
                    mParent.KeyUp += KeyUp;
                }
            }
        }

        public virtual object PropertyBag
        {
            get
            {
                return null;
            }
        }

        public virtual void Started()
        {

        }

        public virtual void Stopping()
        {

        }

        public virtual void AbortAndSetDefaultTool()
        {
            Reset();
            Drawing.SetDefaultBehavior();
        }

        protected virtual void Reset()
        {
        }

        public virtual void KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {

        }

        public virtual void KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
        }

        protected virtual void MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
        }

        protected virtual void MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
        }

        protected virtual void MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
        }

        protected virtual FreePoint CreatePointAtCurrentPosition(
            System.Windows.Point coordinates,
            bool recordAction)
        {
            var result = Factory.CreateFreePoint(Drawing, coordinates);
            AddFigure(result, recordAction);
            return result;
        }

        /// <summary>
        /// Add figure to canvas or figures, depending on the record action parameter.
        /// </summary>
        /// <param name="figureToAdd">The figure to add</param>
        /// <param name="recordAction">Whether or not this action has to be recorded for
        /// use in a transaction (Used for undo/redo).</param>
        protected virtual void AddFigure(
            IFigure figureToAdd,
            bool recordAction)
        {
            if (recordAction)
            {
                Drawing.Add(figureToAdd);
            }
            else
            {
                Drawing.Figures.Add(figureToAdd);
            }
        }

        protected virtual System.Windows.Point Coordinates(System.Windows.Input.MouseEventArgs e)
        {
            return ToLogical(e.GetPosition(Parent));
        }

        #region Coordinates

        protected double CursorTolerance
        {
            get
            {
                return Drawing.CoordinateSystem.CursorTolerance;
            }
        }

        protected double ToPhysical(double logicalLength)
        {
            return Drawing.CoordinateSystem.ToPhysical(logicalLength);
        }

        protected Point ToPhysical(Point point)
        {
            return Drawing.CoordinateSystem.ToPhysical(point);
        }

        protected double ToLogical(double pixelLength)
        {
            return Drawing.CoordinateSystem.ToLogical(pixelLength);
        }

        protected Point ToLogical(Point pixel)
        {
            return Drawing.CoordinateSystem.ToLogical(pixel);
        }

        #endregion
    }
}
